

#include <string.h>

#include "MemAlloc.h"

#include "RingChunk.h"
#include "BlockDeviceConfig.h"
#include "BlockDevice.h"

#include "Debug.h"

#define CHUNK_DBG	0

/*---------------------------------------------------------*/

int Chunk_DoFind(RingChunk_T* chunk, uint8_t dir, uint32_t uid, void* item){
	return RingChunk_FindItem(chunk, dir, uid, item, 0);
}

int Chunk_Find(RingChunk_T* chunk, uint8_t dir, uint32_t uid, void* item){
	BD_Lock();
	int ret = Chunk_DoFind(chunk, dir, uid, item);
	BD_Unlock();
	return ret;
}
/*-----------------------------------*/

int Chunk_DoModify(RingChunk_T* chunk, uint32_t index, void* item){
	//将信息写入新的位置
	if(RingChunk_Append(chunk, item)){
		return -3;
	}
	//随后会删除旧信息，数量并没有增加
	chunk->size--;
	//更新游标
	RingChunk_UpdateInfo(chunk);
	//删除旧信息
	return RingChunk_Write(chunk, index, &(uint32_t){0u}, sizeof(uint32_t));
}


int Chunk_DoSet(RingChunk_T* chunk, void* item){
	uint32_t uid = *(uint32_t*)item;
	if(uid == 0){
	//非法用户信息
		return -2;
	}
	uint32_t index;
	if(RingChunk_FindItemNext(chunk, uid, 0, &index)){
		//用户不存在
		return -1;
	}
	return Chunk_DoModify(chunk, index, item);
}


int Chunk_Set(RingChunk_T* chunk, void* item){
	BD_Lock();
	int ret = Chunk_DoSet(chunk, item);
	BD_Unlock();
	return ret;
}
/*-----------------------------------------*/

int Chunk_DoAdd(RingChunk_T* chunk, uint8_t unique, void* item){
	uint32_t uid = *(uint32_t*)item;

	if(uid == 0){
		//非法信息
		return -2;
	}
	if(RingChunk_IsFull(chunk)){
		//已满
		return -3;
	}
	if(unique && !Chunk_DoFind(chunk, 0, uid, 0)){
		//已存在
		return -1;
	}
	//将信息写入新的位置
	if(RingChunk_Append(chunk, item)){
		return -4;
	}
	//更新游标
	RingChunk_UpdateInfo(chunk);
	return 0;
}

int Chunk_Add(RingChunk_T* chunk, uint8_t unique, void* item){
	BD_Lock();
	int ret = Chunk_DoAdd(chunk, unique, item);
	BD_Unlock();
	return ret;
}

/*-----------------------------------------*/

static int Chunk_DoRemove(RingChunk_T* chunk, uint32_t uid){	
	RingChunk_Remove(chunk, uid);
	RingChunk_UpdateInfo(chunk);
	return 0;
}


int Chunk_Remove(RingChunk_T* chunk, uint32_t uid){	
	BD_Lock();
	int ret = Chunk_DoRemove(chunk, uid);
	BD_Unlock();
	return ret;
}
/*-----------------------------------------*/

int Chunk_DoRead(RingChunk_T* chunk, uint8_t dir, void* item){
	int ret;

	ret = RingChunk_DirFind(chunk, dir, 0, 0);
	if(ret == 0){
		if(dir){
			ret = RingChunk_PreviousReadItem(chunk, item);
		}
		else{			
			ret = RingChunk_CachReadItem(chunk, item);
		}
	}
	if(ret){
		return ret;
	}
	return chunk->itemSize;
}


int Chunk_Read(RingChunk_T* chunk, uint8_t dir, void* item){
	BD_Lock();
	int ret = Chunk_DoRead(chunk, dir, item);
	BD_Unlock();
	return ret;
}
/*-----------------------------------------*/
uint32_t Chunk_Seek(RingChunk_T* chunk, uint32_t offset, uint8_t flag){
	BD_Lock();
	uint32_t cursor = RingChunk_Seek(chunk, offset, flag);
	BD_Unlock();
	return cursor;
}
/*-----------------------------------------*/

int Chunk_Clear(RingChunk_T* chunk){
	BD_Lock();
	RingChunk_Clear(chunk);
	if(chunk->changed){
		RingChunk_UpdateInfo(chunk);
	}
	BD_Unlock();
	return 0;
}

/*-----------------------------------------*/
void Chunk_DoClean(RingChunk_T* chunk){
	RingChunk_T oldChunk = *chunk;	
	RingChunkItem_T* item = (RingChunkItem_T*)mem_alloc(chunk->itemSize);
	if(!item){
		return;
	}
	//游标指向新的空间并初始化
	//计算下一扇区地址
	uint32_t head = chunk->head;
	uint32_t itemPerSector = chunk->itemPerPage * (BD_SectorSize/BD_PageSize);
	head += itemPerSector;
	head = head / itemPerSector * itemPerSector;
	//擦除新区块的第一个扇区
	BD_Erase(RingChunk_Address(chunk, head));
	//在新的区块和旧区块间至少保留一个未写入的元素
	chunk->tail = chunk->head = head + 1;
	chunk->cursor = chunk->tail;
	chunk->size = 0;
	
	RingChunk_Seek(&oldChunk, 0, R_SEEK_START);
	while(oldChunk.size > 0){
		//查找下一个有效信息
		if(RingChunk_FindNext(&oldChunk, 0, 0)){
			break;
		}
		
		RingChunk_CachReadItem(&oldChunk, item);
		if(RingChunk_Append(chunk, item)){
			break;
		}
		oldChunk.size--;
	}
	mem_free(item);
}

void Chunk_Clean(RingChunk_T* chunk){
	BD_Lock();
	if(RingChunk_Footprint(chunk) > chunk->size){
		Chunk_DoClean(chunk);
	}
	if(chunk->changed){
		RingChunk_UpdateInfo(chunk);
	}
	BD_Unlock();
}

/*-----------------------------------------*/
static void Chunk_RemoveDummy(RingChunk_T* chunk){
	if(RingChunk_Footprint(chunk) < 2){
		return;
	}
	uint32_t uid;
	uint32_t i = chunk->head - 1;
	//获取最近添加的uid
	RingChunk_Read(chunk, i, &uid, sizeof(uid));
	if(uid == 0 || uid == 0xFFFFFFFF){
		//uid非法，退出
		return;
	}
	//uid是否有其他旧的副本
	uint32_t index;
	if(RingChunk_FindItemNext(chunk, uid, 0, &index)){
		return;
	}
	if(index == i){
		return;
	}
	//删除旧uid
	uid = 0u;
	RingChunk_Write(chunk, index, &uid, sizeof(uid));
}

/*-----------------------------------------*/
static void DebugChunk(RingChunk_T* chunk){
	MDBG(CHUNK_DBG, "chunk->baseSector=%d\n", chunk->baseSector);
	MDBG(CHUNK_DBG, "chunk->itemCapacity=%d\n", chunk->itemCapacity);
	MDBG(CHUNK_DBG, "chunk->itemPerPage=%d\n", chunk->itemPerPage);
	MDBG(CHUNK_DBG, "chunk->itemSize=%d\n", chunk->itemSize);
	MDBG(CHUNK_DBG, "chunk->head=%d\n", chunk->head);
	MDBG(CHUNK_DBG, "chunk->tail=%d\n", chunk->tail);	
	MDBG(CHUNK_DBG, "chunk->infoVersion=%d\n", chunk->infoVersion);	
	MDBG(CHUNK_DBG, "chunk->infoIndex=%d\n", chunk->infoIndex);	
	MDBG(CHUNK_DBG, "chunk->maxSize=%d\n", chunk->maxSize);	
	MDBG(CHUNK_DBG, "chunk->size=%d\n", chunk->size);	
	MDBG(CHUNK_DBG, "\n");
}

/*
初始化用户信息记录
*/
void Chunk_Init(RingChunk_T* chunk, uint32_t infoSec, uint32_t infoSecNum,
	uint32_t dataSec, uint32_t dataSecNum, uint8_t itemSize, uint32_t maxSize){
	BD_Lock();
	RingChunk_Setup(chunk, infoSec, infoSecNum,
		dataSec, dataSecNum, itemSize, maxSize);
	Chunk_RemoveDummy(chunk);
	BD_Unlock();
	DebugChunk(chunk);
}
/*-----------------------------------------*/

